#include "t_av_packet.h"
#include "t_box_player_ctx.h"

namespace BoxPlayer //[class T_AV_Packet]
{
    class T_AV_PacketPrivate : public QObject
    {
    protected:
        T_DECLARE_PUBLIC(T_AV_Packet)
    protected:
                AVPacket m_av_packet;
        qint64 m_timing;
        bool m_eoq; //[End of Queue]
        int m_frame_no;
        T_Box_Media *m_media;
        ////T_AVPicture *m_video_picture;
        QImage m_video_image;
        T_AVPictureManager *m_picture_man;
    public:
        explicit T_AV_PacketPrivate(T_AV_Packet *me, T_Box_Media *a_media, AVPacket *a_av_packet)
            : T_INIT_QPTR(me)
            , m_eoq(false)
            , m_frame_no(-1)
            , m_media(a_media)
            ////, m_video_picture(0)
            , m_picture_man(NULL)
        {
            if(!a_av_packet)
            {
                m_eoq = true;
                memset(&m_av_packet, 0, sizeof(m_av_packet));
                m_timing = m_media->control()->contentDuration();
            }
            else
            {
                m_eoq = false;
                m_av_packet = *a_av_packet;
                Q_ASSERT(m_media->streamIndex()==m_av_packet.stream_index);
                T_AVRational v_time_base = m_media->time_base();
                m_timing = (v_time_base * m_av_packet.pts * 1000).toLongLong();
            }
        }
        virtual ~T_AV_PacketPrivate()
        {
            if(!m_eoq)
            {
                av_free_packet(&m_av_packet);
            }
            ////delete m_video_picture;
            delete m_picture_man;
        }
    };
    T_AV_Packet::T_AV_Packet(T_Box_Media *a_media, AVPacket *a_av_packet)
    {
        T_INIT_DPTR(T_AV_Packet)(this, a_media, a_av_packet);
    }
    T_AV_Packet::~T_AV_Packet()
    {
    }
    T_Box_Media *T_AV_Packet::media()
    {
        Q_D(T_AV_Packet);
        return d->m_media;
    }
    bool T_AV_Packet::isEnd()
    {
        Q_D(T_AV_Packet);
        return d->m_eoq;
    }
    bool T_AV_Packet::isKeyFrame()
    {
        Q_D(T_AV_Packet);
        return !!(d->m_av_packet.flags & PKT_FLAG_KEY);
    }
    bool T_AV_Packet::isDecoded()
    {
        Q_D(T_AV_Packet);
        ////return (d->m_video_picture != NULL || d->m_picture_man != NULL);
        return (d->m_picture_man != NULL);
    }
    qint64 T_AV_Packet::timing()
    {
        Q_D(T_AV_Packet);
        return d->m_timing;
    }
    int &T_AV_Packet::frameNo()
    {
        Q_D(T_AV_Packet);
        return d->m_frame_no;
    }
    QByteArray T_AV_Packet::decodeAudio(T_AV_AudioDecodeBuffer *a_decode_buff)
    {
        Q_D(T_AV_Packet);
        T_ATOMIC_LOCKER(v_lock, this);
        if(!d->m_media->isAudio()) return QByteArray();
        if(this->isEnd()) return QByteArray();
        AVPacket v_av_packet = d->m_av_packet;
        QByteArray v_result;
        while(v_av_packet.size > 0)
        {
            int v_dec_len = a_decode_buff->decode_array.size();
            int v_enc_len = avcodec_decode_audio3(
                    d->m_media->codecContex(),
                    (int16_t *)a_decode_buff->decode_array.top(),
                    &v_dec_len,
                    &v_av_packet
                    );
            if (v_enc_len < 0)
            {
                /* if error, we skip the frame */
                v_av_packet.size = 0; //force frame finished
            }
            else
            {
                if (d->m_media->codecContex()->sample_fmt == SAMPLE_FMT_S16)
                {
                    v_result.append((char *)a_decode_buff->decode_array.top(), v_dec_len);
                }
                else
                {
                    AVAudioConvert *convCtx =
                            av_audio_convert_alloc(
                                    SAMPLE_FMT_S16,
                                    1,
                                    d->m_media->codecContex()->sample_fmt,
                                    1,
                                    NULL,
                                    0);
                    const void *v_ibuf[6]= {a_decode_buff->decode_array.top()};
                    void *v_obuf[6]= {a_decode_buff->convert_array.top()};
                    int v_istride[6]= {av_get_bits_per_sample_format(d->m_media->codecContex()->sample_fmt)/8};
                    int v_ostride[6]= {av_get_bits_per_sample_format(SAMPLE_FMT_S16)/8};
                    int v_len= v_dec_len/v_istride[0];
                    if (av_audio_convert(convCtx, v_obuf, v_ostride, v_ibuf, v_istride, v_len)<0)
                    {
                        qDebug() << "av_audio_convert() failed";
                        break;
                    }
                    av_audio_convert_free(convCtx);
                    qDebug() << "av_audio_convert() successful";
                    v_dec_len = v_len * v_ostride[0];
                    v_result.append((char *)a_decode_buff->convert_array.top(), v_dec_len);
                }
                v_av_packet.data += v_enc_len;
                v_av_packet.size -= v_enc_len;
            }
        }
        return v_result;
    }
    bool T_AV_Packet::decodeVideoToNull(bool a_make_copy)
    {
        Q_D(T_AV_Packet);
        T_ATOMIC_LOCKER(v_lock, this);
        if(!d->m_media->isVideo()) return false;
        if(this->isEnd()) return false;
        ////if(d->m_video_picture || d->m_picture_man)
        if(d->m_picture_man)
        {
            return true;
        }
        int v_got_pictur;
        if(avcodec_decode_video2(
                d->m_media->codecContex(), //AVCodecContext *avctx,
                d->m_media->frame()->frame(), //AVFrame *picture,
                &v_got_pictur, //int *got_picture_ptr,
                &d->m_av_packet //AVPacket *avpkt
                )<0)
        {
            return false;
        }
        if(!v_got_pictur)
        {
            return false;
        }
        if(a_make_copy)
        {
#if 0x0
#if 0x1
            d->m_video_picture = new T_AVPicture(d->m_media->frame());
#else
            d->m_video_picture = new T_AVPicture();
            d->m_video_picture->copyFrom(d->m_media->frame());
#endif
#endif
            //d->m_picture_man = new T_AVPictureManager_MMF();
            d->m_picture_man = new T_AVPictureManager_Memory();
            d->m_picture_man->proxy()->copyFrom(d->m_media->frame());
            return true;
        }
        return true;
    }
    QImage T_AV_Packet::decodeVideoToQImage(T_AVPicture *a_decode_buff, const QSize &a_size, int a_depth)
    {
        Q_D(T_AV_Packet);
        T_ATOMIC_LOCKER(v_lock, this);
        if(!d->m_media->isVideo())
        {
            d->m_video_image = QImage();
            return d->m_video_image;
        }
        if(this->isEnd())
        {
            d->m_video_image = QImage();
            return d->m_video_image;
        }
        if(!this->decodeVideoToNull())
        {
            d->m_video_image = QImage();
            return d->m_video_image;
        }
        if(!d->m_video_image.isNull())
        {
            return d->m_video_image;
        }
        Q_ASSERT(a_depth==16||a_depth==32);

        //int v_sws_flags = SWS_FAST_BILINEAR;
        int v_sws_flags = SWS_BICUBIC;
        if(d->m_picture_man)
        {
            QSharedPointer<T_AVPictureProxy> v_image = d->m_picture_man->proxy();
            a_decode_buff->resizeForQImage(a_depth, a_size.width(), a_size.height());
            a_decode_buff->scaleFrom(v_image.data(), v_sws_flags);
        }
        else
        {
            //T_AVImage *v_image = d->m_video_picture ? (T_AVImage *)d->m_video_picture : (T_AVImage *)d->m_media->frame();
            a_decode_buff->resizeForQImage(a_depth, a_size.width(), a_size.height());
            a_decode_buff->scaleFrom(d->m_media->frame(), v_sws_flags);
        }

        d->m_video_image = a_decode_buff->toQImage();
#if 0x0
        {
            QPainter v_p(&d->m_video_image);
            int v_font_size = 16;
            QFont v_f("MS UI Gothic", v_font_size, QFont::Bold);
            //QFontInfo fi(v_f);
            //qDebug() << fi.fixedPitch();
            v_f.setFixedPitch(true);
            v_p.setFont(v_f);
            QString v_msg = QString("F=%1 T=%2 %3")
                            .arg(this->frameNo())
                            .arg(this->timing())
                            .arg(this->timing()/1000.0)
                            ;
            //[Time etc]
            int v_diff = 1;
            int v_x_off = 2;
            ////int v_y_off = m_video_image.height() - 4;
            int v_y_off = 24;
            v_p.setPen(Qt::white);
            v_p.drawText(QPoint(v_x_off+v_diff, v_y_off+v_diff), v_msg);
            v_p.setPen(Qt::black);
            v_p.drawText(QPoint(v_x_off, v_y_off), v_msg);
        }
#endif
        Q_ASSERT(!d->m_video_image.isNull());
        return d->m_video_image;
    }
#if 0x0
    bool T_AV_Packet::decodeVideoToMMF()
    {
        Q_D(T_AV_Packet);
        T_ATOMIC_LOCKER(v_lock, this);
        if(!d->m_media->isVideo()) return false;
        if(this->isEnd()) return false;
        if(!d->m_video_picture && !d->m_picture_man)
        {
            int v_got_pictur;
            if(avcodec_decode_video2(
                    d->m_media->codecContex(), //AVCodecContext *avctx,
                    d->m_media->frame()->frame(), //AVFrame *picture,
                    &v_got_pictur, //int *got_picture_ptr,
                    &d->m_av_packet //AVPacket *avpkt
                    )<0)
            {
                return false;
            }
            if(!v_got_pictur)
            {
                return false;
            }
        }
        //d->m_picture_man = new T_AVPictureManager_MMF();
        d->m_picture_man = new T_AVPictureManager_Memory();
        d->m_picture_man->proxy()->copyFrom(d->m_media->frame());
        return true;
    }
#endif
};

namespace BoxPlayer //[class T_AV_Packet_Queue]
{
    class T_AV_Packet_QueuePrivate : public QObject
    {
    protected:
        T_DECLARE_PUBLIC(T_AV_Packet_Queue)
    protected:
        T_Box_Media *m_media;
        bool m_complete;
        qint64 m_max_timing;
    public:
        explicit T_AV_Packet_QueuePrivate(T_AV_Packet_Queue *me, T_Box_Media *a_media)
            : T_INIT_QPTR(me)
            , m_media(a_media)
            , m_complete(false)
            , m_max_timing(0)
        {
            Q_ASSERT(m_media);
        }
        virtual ~T_AV_Packet_QueuePrivate()
        {
        }
    };
    T_AV_Packet_Queue::T_AV_Packet_Queue(T_Box_Media *a_media)
    {
        T_INIT_DPTR(T_AV_Packet_Queue)(this, a_media);
    }
    T_AV_Packet_Queue::~T_AV_Packet_Queue()
    {
    }
    bool T_AV_Packet_Queue::isComplete()
    {
        Q_D(T_AV_Packet_Queue);
        T_ATOMIC_LOCKER(v_lock, this);
        return d->m_complete;
    }
    void T_AV_Packet_Queue::clear()
    {
        Q_D(T_AV_Packet_Queue);
        T_ATOMIC_LOCKER(v_lock, this);
        T_SharedPointerQueue<T_AV_Packet>::clear();
        d->m_complete = false;
        d->m_max_timing = 0;
    }
    void T_AV_Packet_Queue::enqueue(QSharedPointer<T_AV_Packet> a_av_packet)
    {
        Q_D(T_AV_Packet_Queue);
        T_ATOMIC_LOCKER(v_lock, this);
        Q_ASSERT(a_av_packet);Q_ASSERT(!d->m_complete);
        T_SharedPointerQueue<T_AV_Packet>::enqueue(a_av_packet);
        if(a_av_packet->isEnd())
        {
            d->m_complete = true;
        }
        d->m_max_timing = a_av_packet->timing();
        Q_ASSERT(d->m_max_timing>=0);
    }
    qint64 T_AV_Packet_Queue::maxTiming()
    {
        Q_D(T_AV_Packet_Queue);
        T_ATOMIC_LOCKER(v_lock, this);
        Q_ASSERT(d->m_max_timing>=0);
        return d->m_max_timing;
    }
    qint64 T_AV_Packet_Queue::endlessDuration(qint64 a_now_msec)
    {
        Q_D(T_AV_Packet_Queue);
        T_ATOMIC_LOCKER(v_lock, this);
        if(d->m_complete)
        {
            return LONG_LONG_MAX;
        }
        Q_ASSERT(d->m_max_timing>=0);
        qint64 v_ahead = d->m_max_timing - a_now_msec;
        if(v_ahead < 0)
        {
            return 0;
        }
        return v_ahead;
    }
    qint64 T_AV_Packet_Queue::decodedCount(qint64 a_max)
    {
        Q_D(T_AV_Packet_Queue);
        T_ATOMIC_LOCKER(v_lock, this);
        if(d->m_complete)
        {
            if(this->size()==0) return LONG_LONG_MAX;
            if(this->last()->isDecoded()) return LONG_LONG_MAX;
        }
        int v_count = 0;
        for(int i=0; i<this->size(); i++)
        {
            if(!this->value(i)->isDecoded())
            {
                return i;
            }
            v_count = i + 1;
            if(a_max >= 0)
            {
                if(v_count >= a_max)
                {
                    return v_count;
                }
            }
        }
        return this->size();
    }
};
